#include <cassert>

#include "SmallObjectAllocator.h"
#include "TLSFAllocator.h"
#include "Config.h"

namespace MemMgr
{

SmallObjectAllocator::SmallObjectAllocator(size_t iPageSize, size_t iMaxObjectSize) :
	mPoolId(iMaxObjectSize, MAX_ID_NUM),
	mLastDeallocatedIndex(MAX_ID_NUM),
	mPool(),
	mPageSize(iPageSize),
	mMaxObjectSize(iMaxObjectSize)
{
	assert(iMaxObjectSize <= MAX_ID_NUM);
}


void* SmallObjectAllocator::Allocate(size_t iNumBytes)
{
	assert(iNumBytes <= mMaxObjectSize && "Max allocable size exceeded");
	
	if(mPoolId[RetrieveId(iNumBytes)] == MAX_ID_NUM)
	{
		const size_t poolSize = mPool.size();
		mPool.resize(poolSize +1);
		FixedSizeAllocator& newFixedAllocator = mPool.back();
		newFixedAllocator.Initialize(iNumBytes, mPageSize);
		mPoolId[RetrieveId(iNumBytes)] = poolSize;
		return newFixedAllocator.Allocate();
	}

	assert(mPool[mPoolId[RetrieveId(iNumBytes)]].GetBlockSize() == iNumBytes);

	return mPool[mPoolId[RetrieveId(iNumBytes)]].Allocate();
}



void SmallObjectAllocator::Deallocate(void* iPtr, std::size_t iNumBytes)
{
	if(nullptr != iPtr)
	{
		assert(iNumBytes <= mMaxObjectSize && "Max deallocable size exceeded");

		assert(mPoolId[RetrieveId(iNumBytes)] != MAX_ID_NUM);
		assert(mPool[mPoolId[RetrieveId(iNumBytes)]].GetBlockSize() == iNumBytes);

		mPool[mPoolId[RetrieveId(iNumBytes)]].Deallocate(iPtr);
	}
}

size_t SmallObjectAllocator::GetMaxObjectSize() 
{
	return mMaxObjectSize;
}

bool SmallObjectAllocator::Deallocate(void* iPtr)
{
	if(nullptr != iPtr)
	{
		Chunk* chunk;

		if(mLastDeallocatedIndex != MAX_ID_NUM && (chunk = mPool[mLastDeallocatedIndex].SearchChunk(iPtr)))
		{
			mPool[mLastDeallocatedIndex].Deallocate(iPtr, chunk);
			return true;
		}

		AtomSizeU i = 0;

		for(Pool::iterator it = mPool.begin(); it != mPool.end(); ++it)
		{
			chunk = (*it).SearchChunk(iPtr);
			if(chunk)
			{
				(*it).Deallocate(iPtr, chunk);
				mLastDeallocatedIndex = i;
				return true;
			}
			++i;
		}
	}
	
	//assert(true && "Cannot deallocate an address never allocated!");	}

	return false;
}

SmallObjectAllocator& SmallObjectAllocator::GetSingleton()
{
	MemMgr::TLSFAllocator::GetSingleton();	// forgive us, time was missing :(
	static SmallObjectAllocator instance(PAGE_SIZE, SMALL_OBJ_MAX_SIZE);
	return instance;
}

SmallObjectAllocator* SmallObjectAllocator::GetSingletonPtr()
{
	static SmallObjectAllocator* instancePtr = &GetSingleton();
	return instancePtr;
}

}